#include "arch.h"
#include "mhn-regs.h"
#include "config.h"
#include "command.h"
#include "flash.h"
#include "serial.h"
#include "time.h"
#include "main.h"
#include "util.h"
#include "irq.h"
#include "xmodem.h"
#include "ymodem.h"
#include "tat.h"
#include "crc16.h"
#include "usb_serial_download.h"
#include "doc_op.h"
#include "KeypadTest.h"
#include "nandflash.h"

//#define TAT 0

//The following is used to update the whole Image by USB

volatile int iimageupdate_by_usb=0;
unsigned int verbose_level = 1;

extern int udownload_cmd(u32 *destAddress, u32 bufLen);
extern int UsbDownloadImageEx();
extern int md5_stream (char *start_address, int length);
void BootKernel(int arg0, int arg1);
void Download(u8* address);
void Jump(u32* address);
void JumptoTAT(void);
void ShowVersion(void);

void ResetTerminal(void);
void PrintSerialSpeed(serial_baud_t speed);
void ExecuteCommand(u8* commandline);
void hw_reset(void);
void ArmSys();
void tat_menu();

int g_bWrite = 1;

#ifdef TAT
unsigned char rts_signal;
int rts_try;
#endif

void ShowUsbPinState(void)
{
//    printk("GPIO0  (B_VBUS_IN)       : Dir(%d), Level(%d), Alt(%d) \n", (GPDR0 >> 0  & 1), (GPLR0 >> 0  & 1), (GAFR0_L >> 0  & 3));
//    printk("GPIO99 (B_WALL_CHARGER)  : Dir(%d), Level(%d), Alt(%d) \n", (GPDR3 >> 3  & 1), (GPLR3 >> 3  & 1), (GAFR3_L >> 6  & 3));
//    printk("GPIO80 (B_USB_OTG_ID)    : Dir(%d), Level(%d), Alt(%d) \n", (GPDR2 >> 16 & 1), (GPLR2 >> 16 & 1), (GAFR2_U >> 0  & 3));
//    printk("GPIO88 (B_USB_FULLSPEED) : Dir(%d), Level(%d), Alt(%d) \n", (GPDR2 >> 24 & 1), (GPLR2 >> 24 & 1), (GAFR2_U >> 16 & 3));
//    printk("GPIO107(B_CHARGE_STAT1)  : Dir(%d), Level(%d), Alt(%d) \n", (GPDR3 >> 11 & 1), (GPLR3 >> 11 & 1), (GAFR3_L >> 22 & 3));
//    printk("GPIO13 (B_CHARGE_STAT2)  : Dir(%d), Level(%d), Alt(%d) \n", (GPDR0 >> 13 & 1), (GPLR0 >> 13 & 1), (GAFR0_L >> 26 & 3));
}

void Xs_LCD_Init();
void Display_Logo();
int bNormalBoot = 0;
void backlight_open();

void check_onkey()
{
    if(!bNormalBoot) {
        //printk("\n\n bNormalBoot = %x, GPLR0 = %x, GEDR0 = %x, jiffies = %d\n\n\n",
        //    bNormalBoot, GPLR0 & 3, GEDR0 & 3, jiffies);

        if (jiffies < 150) {//1-3s
            //shutdown when usb cable not pluged AND onkey not pressed
            printk("should shutdown!\n");
        }
        else {
//            printk("onkey pressed for more than 1s, start now\n");
//            backlight_open();
            bNormalBoot = TRUE;
        }
    }
}
void init_monahans_nand_driver(void);

//#include <bo_utils.h>

#define KEY_TIMEOUT                                        0x54
#define KEY_AUTOLOAD                                    KEY_TIMEOUT

#define KEY_USB_DOWNLOAD_MODE        0xa4   //Key big circle center
#define KEY_BOOTLOADER_CONSOLE       0x10   //Key B

static inline void EnterAutoloadMode()
{
           /* Jump to kernel */
    SerialOutputString("\nAuto Boot Entered\n");
    if (cmd_nand_read(KERNEL_RAM_START, FLASHOFF, KERNELLEN)) {
        cli();
        BootKernel(BOOT_ARG0, BOOT_ARG1);
    }
    return;
}

void EnterBootloaderConsole(void);

void backlight_on();
void  c28_main(void)
{
    backlight_on();
    unsigned char commandline[128] = {0,};

    printf("\nc28_main():\n");
    if (!SerialInputBlock_msec((char*)commandline,  1, 10)) {
        printf("Armsys...\n");
        ArmSys();
    }
    //    Xs_LCD_Init();
    //    Display_Logo();
    int key = 0;
    key=wls_keypad_getkey();//Init Already in board.cpp
    printk("key=0x%x", key);
    switch(key){
        case KEY_USB_DOWNLOAD_MODE:
                if (key != 0x54/* 'T' */) {//Any key to download os image
                    printk("Get key=%x\n", key);
                    cli();
                    g_bWrite = 0;
                    if (UsbDownloadImageEx()) {
                        g_bWrite = 1;
                        BootKernel(BOOT_ARG0, BOOT_ARG1);
                    }
                        sti();
                }
                break;

        case KEY_BOOTLOADER_CONSOLE:
                EnterBootloaderConsole();
                break;

        default:
                //EnterAutoloadMode();
                EnterBootloaderConsole();//Used For ShiJiang,Later will restore to above
                break;
    }

}
void SerialInit(serial_baud_t baudrate);
int Xo_Main();
int __cdecl c_main()
{
    SerialInit(baud_115200);
    Xo_Main();
    c28_main();
    return 0;
}

void de_mem_test();

void ShowVersion()
{
    printk( "\n\tElastos2.0 Bootloader\n\tVersion:%s, Time Stamp:%s\n", BOOTLOADVERSION, COMPILE_TIME_STAMP );
}

/*
  Jump to the pre-copied kernel in the RAM
*/
typedef struct BootInfo {
    unsigned int LoadAddress;
    unsigned int ImageBase;
    unsigned int ImageSize;
    unsigned int MemorySize;
    unsigned int WriteMagic;
    unsigned int Reserved1;
    unsigned int Reserved2;
    unsigned int Reserved3;
} BootInfo;

typedef struct PackageInfo {
    unsigned int    uReserved;
    void            *modules;
    unsigned int    uNumberOfModules;
    unsigned int    uPackageSize;
    char            *szCommandLine;
} PackageInfo;

void BootKernel(int arg0, int arg1)
{
    BootInfo *pLoadInfo = (BootInfo *)RAM_START;
    int *pEntry;
    void (*theKernel)(int zero, int arch);
    // find kernel entry
    pEntry = (int*)(KERNEL_RAM_START + 0x3c);

    if ((*(int *)(*pEntry + KERNEL_RAM_START)) != 0x00004550) {
        printk("Kernel is not PE format!\n");
        return;
    }
    pEntry = (int*)(KERNEL_RAM_START + (*pEntry) + 0x28);
    theKernel = (void*)((*pEntry) + KERNEL_RAM_START);

    jiffies = 1000;
    check_onkey();

    // set kernel arguments
    if (!arg1) {
        arg1 = (int)pLoadInfo;
        pLoadInfo->WriteMagic = 0;
        pLoadInfo->MemorySize = 0x4000000;
        pLoadInfo->LoadAddress = KERNEL_RAM_START;
        pLoadInfo->ImageBase = 0xc0020000;
        pEntry = (int*)(KERNEL_RAM_START + 0x8);
        pLoadInfo->ImageSize = ((PackageInfo *)(*pEntry + KERNEL_RAM_START))\
                             ->uPackageSize;
    }
/*  printk("loadinfo(%08x):WriteMagic%08x\n"
           "MemorySize%08x\n"
           "LoadAddress%08x\n"
           "ImageBase%08x\n"
           "ImageSize%08x\n",
        pLoadInfo,
        pLoadInfo->WriteMagic,
        pLoadInfo->MemorySize,
        pLoadInfo->LoadAddress,
        pLoadInfo->ImageBase,
        pLoadInfo->ImageSize);*/

    printk("jump to kernel\n");
    cli();
    theKernel(arg0,arg1);
    sti();

    printk("Hey, the kernel returned! This should not happen.\r");
}

void Jump(u32* address)
{
    void (*Func)(void)= (void(*)(void))address;

    (*Func)();

    /* To return to this point, code should save every registes used,
     * lr, etc. and return to the lr and should never modify the SP
     */
}

static void WatchdogReset()
{
    OWER = 1;
    OSMR3 = OSCR + 0x200;

    printk("Can not reset.Please power off\n");
    while (TRUE) {
    }
}

void hw_reset(void)
{
    int start_time;
    start_time = TimerGetTime();

    printk("Auto reset...\n");
    while((TimerGetTime()-start_time) < 2);
    WatchdogReset();
}

void Boot()
{
    Jump((u32*)KERNEL_RAM_START);
}

void ArmSys()
{
    unsigned long packsize,flashoffset;
    unsigned long buffer = KERNEL_RAM_START;
    long IsWriteFlash;
    int read = 0;
    SerialOutputRawByte('0');
    SerialOutputRawByte('1');
    SerialOutputRawByte('2');
    SerialOutputRawByte('3');

    // Send query signal,ask for file length
    SerialOutputRawByte('?');

    //  Get file length nowait
    flashoffset = SerialInputDWordWithTimeout();
    packsize = SerialInputDWordWithTimeout();
    IsWriteFlash = packsize & 0x80000000;
    packsize = packsize & ~0x80000000;
    printf("get info %x,%x\n",flashoffset,packsize);
    if (packsize) {    // get image from armsys
        // end ready signal
        SerialOutputRawByte('<');

        // receive the file data
        read = SerialInputBlock_msec((char*)buffer, packsize, 1000/*20 minutes*/);

        SerialOutputRawByte('>');
        printf("have got %x bytes\n",read);
        if (flashoffset != 0x20000){//get kernel from armsys
            flashoffset = FLASHOFF;
        }
        else flashoffset = 0;
        // jump to run program if u don't need write flash
        if (IsWriteFlash) {
            SerialOutputString("Write Image to Nandflash\n");
            if(cmd_nand_write(buffer, flashoffset, packsize) < packsize)
                    SerialOutputString("Write Image Failed!\n");
                  else
                    SerialOutputString("Write Image Succeeded!\n");
        }
        // image is already in the dram
        if (flashoffset == 0){//run eldr or test
              if (IsWriteFlash)WatchdogReset();
            else Boot();
        }
        else BootKernel(0, 0);
    }
}

